﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.Windows;

namespace PDX
{
    /// <summary>
    /// Selects a conventional DataTemplate given an <see cref="IPropertyModel"/>
    /// and a <see cref="Element"/>.
    /// </summary>
    public class DefaultConventionalTemplateSelector : DataTemplateSelector
    {
        /// <summary>
        /// Selects a data template based on a naming convention.
        /// </summary>
        /// <param name="item">The data object for which to select the template.</param>
        /// <param name="container">The data-bound object.</param>
        /// <returns>
        /// Returns a <see cref="T:System.Windows.DataTemplate"/> or null. The default value is null.
        /// </returns>
        /// <remarks>
        /// The naming convention is "pdx{ControlType}[Readonly]{PropertyType}".
        /// For example, "pdxEditorString" means the <see cref="Element"/> is an
        /// <see cref="Editor"/> and the property model type is a string (and the
        /// property is writable). In this case, we are going to try to get a
        /// DataTemplate with the key "pdxEditorString". If that's not found, we try
        /// "pdxEditor". Finally, we try "pdx".
        /// </remarks>
        public override DataTemplate SelectTemplate(object item, DependencyObject container)
        {
            var element = container as FrameworkElement;
            var pm = item as IPropertyModel;

            if (element == null || pm == null)
            {
                return base.SelectTemplate(item, container);
            }

            var res = pm.Resolution;

            // let's build a string in this format:
            // pdx<ControlType>[<Readonly>]<PropertyType>
            // and look for the most specific down to the least
            LinkedList<string> parts = new LinkedList<string>();

            // We can say pdxLabeller is a TextBlock bound to DisplayName.
            //    - with an asterisk whose Visiblility bound to IsRequired.
            //    - basically, Labellers always look the same
            // We can say pdxValidator is a TextBlock bound to Validation.Message.
            //    - Visibility bound to Validation.IsInvalid
            //    - basically, Validators all look the same
            // We can say pdxEditorReadonly is a TextBlock bound to Value.
            //    - many Editors look the same for readonly properties
            // But what about custom property types where we want them to look the same readonly as writable?

            // What about enums? or scenarios requiring drop-downs?

            // We could do a permissions check and just say Readonly - even though the Property isn't RO, we want it to be RO for the user?
            // More likely, we would bind IsEnabled to Permission.CanWrite

            parts.AddLast("pdx");
            parts.AddLast(element.GetType().Name);

            if (res.Property.GetSetMethod() == null)
            {
                parts.AddLast("Readonly");
            }

            if (res.Property.PropertyType.IsEnum)
            {
                parts.AddLast("Enum");
            }

            parts.AddLast(res.Property.PropertyType.Name);

            while (parts.Count > 0)
            {
                var dtName = Concat(parts);
                var dt = element.TryFindResource(dtName) as DataTemplate;
                if (dt != null)
                {
                    return dt;
                }

                parts.RemoveLast();
            }

            return base.SelectTemplate(item, container);
        }

        string Concat(LinkedList<string> parts)
        {
            StringBuilder builder = new StringBuilder();
            foreach (var part in parts)
            {
                builder.Append(part);
            }
            return builder.ToString();
        }
    }
}
